Iterator pattern

Iterator pattern

假设有两个餐厅,它们各自的菜单组织形式不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MenuItem {
String name;
String description;
boolean vegetarian;
double price;

public MenuItem(String name,
String description,
boolean vegetarian,
double price)
{
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}

public String getName() {
return name;
}

public String getDescription() {
return description;
}

public double getPrice() {
return price;
}

public boolean isVegetarian() {
return vegetarian;
}
}
1
2
3
4
5
6
7
8
public class PanackeHouseMenue {
ArrayList menuItems;
...
}

public class DinerMenu {
MenuItem[] menuItems;
}

当遍历菜单时,我们需要提供两个方法遍历两种菜单,使系统变得复杂化。如果需要增加一种新的菜单,需要修改前面的很多代码,造成难以维护。

定义迭代器模式

这里我们可以把遍历封装起来,让每个类提供相同的方法遍历它自己的元素。外部客户只管调用这些方法来遍历,而不必直到内部是如何实现的。且由于所有类都提供同样的遍历方法,降低的复杂性,使得系统容易扩展和修改。

迭代其模式提供一种同一的方法顺序访问一个聚合对象中的各个元素,而由不暴露其内部的表示。把遍历的任务交给迭代器,而不是聚合对象上,这样简化了聚合对象的接口和实现,也满足了单一责任原则:一个类应该只负责一个职责,并只有一个引起变化的原因。

Design_pattern_iterator_1

1
2
3
4
public interface Iterator {
boolean hasNext();
Object next();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;

public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}

public Object next() {
MenuItem menuItem = items[position];
position = position + 1;
return menuItem;
}

public boolean hasNext() {
if (position >= items.length || items[position] == null) {
return false;
} else {
return true;
}
}
}

public class PancakeHouseMenuIterator implements Iterator {
ArrayList items;
int position = 0;

public PancakeHouseMenuIterator(ArrayList items) {
this.items = items;
}

public Object next() {
Object object = items.get(position);
position = position + 1;
return object;
}

public boolean hasNext() {
if (position >= items.size()) {
return false;
} else {
return true;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
public class DinerMenu implements Menu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;

public DinerMenu() {
menuItems = new MenuItem[MAX_ITEMS];

addItem("Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99);
addItem("BLT",
"Bacon with lettuce & tomato on whole wheat", false, 2.99);
addItem("Soup of the day",
"Soup of the day, with a side of potato salad", false, 3.29);
addItem("Hotdog",
"A hot dog, with saurkraut, relish, onions, topped with cheese",
false, 3.05);
addItem("Steamed Veggies and Brown Rice",
"Steamed vegetables over brown rice", true, 3.99);
addItem("Pasta",
"Spaghetti with Marinara Sauce, and a slice of sourdough bread",
true, 3.89);
}

public void addItem(String name, String description,
boolean vegetarian, double price)
{
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.err.println("Sorry, menu is full! Can't add item to menu");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems = numberOfItems + 1;
}
}

public MenuItem[] getMenuItems() {
return menuItems;
}

public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
}

// other menu methods here
}

public class PancakeHouseMenu implements Menu {
ArrayList menuItems;

public PancakeHouseMenu() {
menuItems = new ArrayList();

addItem("K&B's Pancake Breakfast",
"Pancakes with scrambled eggs, and toast",
true,
2.99);

addItem("Regular Pancake Breakfast",
"Pancakes with fried eggs, sausage",
false,
2.99);

addItem("Blueberry Pancakes",
"Pancakes made with fresh blueberries",
true,
3.49);

addItem("Waffles",
"Waffles, with your choice of blueberries or strawberries",
true,
3.59);
}

public void addItem(String name, String description,
boolean vegetarian, double price)
{
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}

public ArrayList getMenuItems() {
return menuItems;
}

public Iterator createIterator() {
return new PancakeHouseMenuIterator(menuItems);
}

public String toString() {
return "Objectville Pancake House Menu";
}

// other menu methods here
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Waitress {
PancakeHouseMenu pancakeHouseMenu;
DinerMenu dinerMenu;

public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}

public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();

System.out.println("MENU\n----\nBREAKFAST");
printMenu(pancakeIterator);
System.out.println("\nLUNCH");
printMenu(dinerIterator);
}

private void printMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDescription());
}
}

public void printVegetarianMenu() {
printVegetarianMenu(pancakeHouseMenu.createIterator());
printVegetarianMenu(dinerMenu.createIterator());
}

public boolean isItemVegetarian(String name) {
Iterator breakfastIterator = pancakeHouseMenu.createIterator();
if (isVegetarian(name, breakfastIterator)) {
return true;
}
Iterator dinnerIterator = dinerMenu.createIterator();
if (isVegetarian(name, dinnerIterator)) {
return true;
}
return false;
}


private void printVegetarianMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
if (menuItem.isVegetarian()) {
System.out.print(menuItem.getName());
System.out.println("\t\t" + menuItem.getPrice());
System.out.println("\t" + menuItem.getDescription());
}
}
}

private boolean isVegetarian(String name, Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
if (menuItem.getName().equals(name)) {
if (menuItem.isVegetarian()) {
return true;
}
}
}
return false;
}

通过迭代器,我们可以很容易的新增菜单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class CafeMenu implements Menu {
Hashtable menuItems = new Hashtable();

public CafeMenu() {
addItem("Veggie Burger and Air Fries",
"Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
true, 3.99);
addItem("Soup of the day",
"A cup of the soup of the day, with a side salad",
false, 3.69);
addItem("Burrito",
"A large burrito, with whole pinto beans, salsa, guacamole",
true, 4.29);
}

public void addItem(String name, String description,
boolean vegetarian, double price)
{
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.put(menuItem.getName(), menuItem);
}

public Hashtable getItems() {
return menuItems;
}

public Iterator createIterator() {
return menuItems.values().iterator();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class Waitress {
Menu pancakeHouseMenu;
Menu dinerMenu;
Menu cafeMenu;

public Waitress(Menu pancakeHouseMenu, Menu dinerMenu, Menu cafeMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
this.cafeMenu = cafeMenu;
}

public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();
Iterator cafeIterator = cafeMenu.createIterator();

System.out.println("MENU\n----\nBREAKFAST");
printMenu(pancakeIterator);
System.out.println("\nLUNCH");
printMenu(dinerIterator);
System.out.println("\nDINNER");
printMenu(cafeIterator);
}

private void printMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDescription());
}
}

public void printVegetarianMenu() {
System.out.println("\nVEGETARIAN MENU\n---------------");
printVegetarianMenu(pancakeHouseMenu.createIterator());
printVegetarianMenu(dinerMenu.createIterator());
printVegetarianMenu(cafeMenu.createIterator());
}

public boolean isItemVegetarian(String name) {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
if (isVegetarian(name, pancakeIterator)) {
return true;
}
Iterator dinerIterator = dinerMenu.createIterator();
if (isVegetarian(name, dinerIterator)) {
return true;
}
Iterator cafeIterator = cafeMenu.createIterator();
if (isVegetarian(name, cafeIterator)) {
return true;
}
return false;
}


private void printVegetarianMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
if (menuItem.isVegetarian()) {
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDescription());
}
}
}

private boolean isVegetarian(String name, Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
if (menuItem.getName().equals(name)) {
if (menuItem.isVegetarian()) {
return true;
}
}
}
return false;
}
}

目前在菜单增加时,遍历菜单的方法不需要再修改。但是我们需要单独管理这些菜单,而不是能够讲它们也聚合在一起作为一个单独的聚合对象管理,通过使用基类List可以部分改进该,但是我们仍然无法在菜单里增加子菜单,也就是层级模式。

Design_pattern_iterator_2

Design_pattern_iterator_3

组合模式

这时我们可以使用组合模式解决这个难题:

定义组合模式

组合模式允许我们将组合对象组合成树形结构来表现”整体/部分”层次结构。组合能够让客户以一致的方式处理个别对象以及对象组合。

Design_pattern_iterator_4

Design_pattern_iterator_5

这里我们使用组合设计菜单:

Design_pattern_iterator_6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public abstract class MenuComponent {

public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}

public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}

public void print() {
throw new UnsupportedOperationException();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price;

public MenuItem(String name,
String description,
boolean vegetarian,
double price)
{
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}

public String getName() {
return name;
}

public String getDescription() {
return description;
}

public double getPrice() {
return price;
}

public boolean isVegetarian() {
return vegetarian;
}

public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class Menu extends MenuComponent {
ArrayList menuComponents = new ArrayList();
String name;
String description;

public Menu(String name, String description) {
this.name = name;
this.description = description;
}

public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}

public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}

public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}

public String getName() {
return name;
}

public String getDescription() {
return description;
}

public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");

Iterator iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent =
(MenuComponent)iterator.next();
menuComponent.print();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
public class Waitress {
MenuComponent allMenus;

public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}

public void printMenu() {
allMenus.print();
}
}

public class MenuTestDrive {
public static void main(String args[]) {
MenuComponent pancakeHouseMenu =
new Menu("PANCAKE HOUSE MENU", "Breakfast");
MenuComponent dinerMenu =
new Menu("DINER MENU", "Lunch");
MenuComponent cafeMenu =
new Menu("CAFE MENU", "Dinner");
MenuComponent dessertMenu =
new Menu("DESSERT MENU", "Dessert of course!");
MenuComponent coffeeMenu = new Menu("COFFEE MENU", "Stuff to go with your afternoon coffee");

MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");

allMenus.add(pancakeHouseMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);

pancakeHouseMenu.add(new MenuItem(
"K&B's Pancake Breakfast",
"Pancakes with scrambled eggs, and toast",
true,
2.99));
pancakeHouseMenu.add(new MenuItem(
"Regular Pancake Breakfast",
"Pancakes with fried eggs, sausage",
false,
2.99));
pancakeHouseMenu.add(new MenuItem(
"Blueberry Pancakes",
"Pancakes made with fresh blueberries, and blueberry syrup",
true,
3.49));
pancakeHouseMenu.add(new MenuItem(
"Waffles",
"Waffles, with your choice of blueberries or strawberries",
true,
3.59));

dinerMenu.add(new MenuItem(
"Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat",
true,
2.99));
dinerMenu.add(new MenuItem(
"BLT",
"Bacon with lettuce & tomato on whole wheat",
false,
2.99));
dinerMenu.add(new MenuItem(
"Soup of the day",
"A bowl of the soup of the day, with a side of potato salad",
false,
3.29));
dinerMenu.add(new MenuItem(
"Hotdog",
"A hot dog, with saurkraut, relish, onions, topped with cheese",
false,
3.05));
dinerMenu.add(new MenuItem(
"Steamed Veggies and Brown Rice",
"Steamed vegetables over brown rice",
true,
3.99));

dinerMenu.add(new MenuItem(
"Pasta",
"Spaghetti with Marinara Sauce, and a slice of sourdough bread",
true,
3.89));

dinerMenu.add(dessertMenu);

dessertMenu.add(new MenuItem(
"Apple Pie",
"Apple pie with a flakey crust, topped with vanilla icecream",
true,
1.59));

dessertMenu.add(new MenuItem(
"Cheesecake",
"Creamy New York cheesecake, with a chocolate graham crust",
true,
1.99));
dessertMenu.add(new MenuItem(
"Sorbet",
"A scoop of raspberry and a scoop of lime",
true,
1.89));

cafeMenu.add(new MenuItem(
"Veggie Burger and Air Fries",
"Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
true,
3.99));
cafeMenu.add(new MenuItem(
"Soup of the day",
"A cup of the soup of the day, with a side salad",
false,
3.69));
cafeMenu.add(new MenuItem(
"Burrito",
"A large burrito, with whole pinto beans, salsa, guacamole",
true,
4.29));

cafeMenu.add(coffeeMenu);

coffeeMenu.add(new MenuItem(
"Coffee Cake",
"Crumbly cake topped with cinnamon and walnuts",
true,
1.59));
coffeeMenu.add(new MenuItem(
"Bagel",
"Flavors include sesame, poppyseed, cinnamon raisin, pumpkin",
false,
0.69));
coffeeMenu.add(new MenuItem(
"Biscotti",
"Three almond or hazelnut biscotti cookies",
true,
0.89));

Waitress waitress = new Waitress(allMenus);

waitress.printMenu();
}
}

组合模式和组合迭代器

递归实现:

1
2
3
4
5
6
public class MenuItem extends MenuComponent {
// 其他部分不需要改动
public Iterator createIterator() {
return new NullIterator();
}
}
1
2
3
4
5
6
public class Menu extends MenuComponent {
// 其他部分不需要改动
public Iterator createIterator() {
return new CompositeIterator(menuComponents.iterator());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class NullIterator implements Iterator {

public Object next() {
return null;
}

public boolean hasNext() {
return false;
}

public void remove() {
throw new UnsupportedOperationException();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class CompositeIterator implements Iterator {
Stack stack = new Stack();

public CompositeIterator(Iterator iterator) {
stack.push(iterator);
}

public Object next() {
if (hasNext()) {
Iterator iterator = (Iterator) stack.peek();
MenuComponent component = (MenuComponent) iterator.next();
if (component instanceof Menu) {
stack.push(component.createIterator());
}
return component;
} else {
return null;
}
}

public boolean hasNext() {
if (stack.empty()) {
return false;
} else {
Iterator iterator = (Iterator) stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;
}
}
}

public void remove() {
throw new UnsupportedOperationException();
}
}